<template>
  <div class="waterfall-container">
    <div 
      v-for="(item, index) in codeItems" 
      :key="index" 
      class="code-card"
    >
      <div class="card-header">
        <h3>{{ item.title }}</h3>
        <button 
          class="copy-btn"
          @click="copyCode(item.code)"
          title="复制代码"
        >
          <svg viewBox="0 0 24 24" width="16" height="16">
            <path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"/>
          </svg>
        </button>
      </div>
      <p class="card-description">{{ item.description }}</p>
      <pre class="code-block"><code>{{ item.code }}</code></pre>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const codeItems = ref([
  {
    title: '防止 console 重写',
    description: '有的时候去做一些网站逆向的时候，会发现网站会把 console 给重写，导致我们的代码失效，更狠的是他们会把 console 重写为无限循环的代码卡死浏览器，这个时候我们可以使用这个代码来防止 console 重写',
    code: `// -------- 防护1：冻结console对象（防止被重写） -------
// 备份原生console对象（部分浏览器可能不允许修改原生console，需提前检查）
const originalConsole = { ...console };  // 浅拷贝原生console的属性（如log、error等）

// 尝试冻结console对象（阻止属性被修改/删除/新增）
if (Object.isFrozen(console) === false) {
    Object.freeze(console);  // 冻结后无法修改console的属性
}

// ---------- 防护2：启用严格模式（阻止未声明变量） ------
'use strict';  // 严格模式会禁止隐式全局变量（如恶意代码中的‘i’未声明会报错）

// ------- 防护3：重写history.pushState（限制调用频率） ---------
const originalPushState = history.pushState;
let pushStateCount = 0;  // 记录pushState调用次数
const MAX_CALLS_PER_SECOND = 10;  // 每秒最多允许调用10次（可根据需求调整）

history.pushState = function(...args) {
    const now = Date.now();
    // 每秒重置计数（防时间溢出）
    if (now - (history.pushState.lastCheckTime || 0) > 1000) {
        pushStateCount = 0;
        history.pushState.lastCheckTime = now;
    }

    // 超过频率限制则拦截调用
    if (pushStateCount++ >= MAX_CALLS_PER_SECOND) {
        console.warn('检测到异常history.pushState调用，已拦截');
        return;  // 不执行原生pushState
    }

    // 正常调用时执行原生方法
    return originalPushState.apply(this, args);
};

// ------ 防护4：检测console是否被篡改（实时监控） -------
function checkConsoleTampered() {
    // 检查console是否被替换为新对象（原生console的toString返回[object Console]）
    if (console.toString() !== '[object Console]') {
        console.error('警告：console对象被非法替换！');
        // 尝试恢复为原生console（需浏览器支持）
        Object.defineProperty(window, 'console', {
            value: originalConsole,
            writable: false,  // 防止再次被修改
            configurable: false
        });
        return true;
    }

    // 检查console.log是否为原生函数（原生函数的toString包含[native code]）
    const isLogTampered = !/\[native code\]/.test(console.log.toString());
    if (isLogTampered) {
        console.error('警告：console.log被非法重写！');
        // 恢复原生log方法
        console.log = originalConsole.log;
        return true;
    }

    return false;
}

// 每1秒检测一次console状态（实时监控）
setInterval(checkConsoleTampered, 1000);`
  },
  {
    title: 'Hook Cookie 油猴脚本',
    description: ' Hook Cookie 油猴脚本，我们去爬取一些网站的时候，这个网站的 cookie 中参数是动态， 可以通过 hook 定位加密 cookie 时的代码位置， 然后我们就可以通过这个代码位置去获取 cookie 了',
    code: `// ==UserScript==
// @name         Cookie Change Logger
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Log when cookie is changed, along with cookie content and code location
// @author       Your Name
// @match        *://*/*
// @grant        unsafeWindow
// ==/UserScript==

// 使用立即执行函数创建一个独立的作用域，防止变量污染全局作用域
// 并且启用严格模式，使代码遵循更严格的语法和执行规则
(function () {
    'use strict';

    // Tampermonkey中通过unsafeWindow获取全局window对象
    // 这是因为Tampermonkey脚本运行在一个沙箱环境中，需要特殊方式访问全局对象
    var win = unsafeWindow;

    // 获取document对象上cookie属性的属性描述符
    // 属性描述符包含了该属性的各种特性，如getter、setter、可枚举性等
    var originalDescriptor = Object.getOwnPropertyDescriptor(win.document, 'cookie');

    // 如果成功获取到属性描述符，则提取其getter函数
    // 否则，提供一个默认的getter函数，该函数返回空字符串
    var originalCookieGetter = originalDescriptor? originalDescriptor.get : function () { return ''; };

    // 如果成功获取到属性描述符，则提取其setter函数
    // 否则，提供一个默认的setter函数，该函数不执行任何操作
    var originalCookieSetter = originalDescriptor? originalDescriptor.set : function () {};

    // 使用Object.defineProperty方法重新定义document.cookie的属性
    // 这将覆盖原有的cookie属性定义，并允许我们插入自定义的逻辑
    Object.defineProperty(win.document, 'cookie', {
        // 定义新的getter函数
        get: function () {
            // 调用原始的getter函数，确保获取cookie的原有功能不受影响
            // 通过apply方法将当前上下文（this）和参数（arguments）传递给原始getter
            return originalCookieGetter.apply(this, arguments);
        },
        // 定义新的setter函数
        set: function (value) {
            // 用于存储调用栈信息的数组
            var stack = [];
            try {
                // 抛出一个错误，目的是获取调用栈信息
                // 当捕获到这个错误时，错误对象的stack属性将包含调用栈信息
                throw new Error();
            } catch (e) {
                // 将错误对象的stack属性按换行符分割成数组，得到调用栈信息
                stack = e.stack.split('\n');
            }
            // 获取当前的cookie值（在设置新值之前）
            var currentCookie = win.document.cookie;
            // 在控制台打印新的cookie内容
            console.log('Cookie has been changed. New Cookie content:', value);
            // 在控制台打印cookie改变的代码位置调用栈信息
            console.log('Cookie change code location stack:', stack);
            // 调用原始的setter函数，确保设置cookie的原有功能不受影响
            // 通过apply方法将当前上下文（this）和新的cookie值（value）传递给原始setter
            originalCookieSetter.apply(this, [value]);
        },
        // 设置cookie属性为可枚举的
        // 这意味着在使用for...in循环或Object.keys()等方法枚举对象属性时，cookie属性会被包含在内
        enumerable: true,
        // 设置cookie属性为可配置的
        // 这意味着可以再次使用Object.defineProperty方法重新定义该属性，或者删除该属性
        configurable: true
    });
})();`
  }
]);

const copyCode = (code) => {
  navigator.clipboard.writeText(code)
    .then(() => {
      alert('代码已复制到剪贴板');
    })
    .catch(err => {
      console.error('复制失败:', err);
    });
};
</script>

<style scoped>
.waterfall-container {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(600px, 1fr));
  gap: 20px;
  padding: 20px;
}

.code-card {
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
  padding: 16px;
  position: relative;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12px;
}

.copy-btn {
  background: none;
  border: none;
  cursor: pointer;
  padding: 4px;
  opacity: 0.6;
  transition: opacity 0.2s;
}

.copy-btn:hover {
  opacity: 1;
}

.card-description {
  color: #666;
  margin-bottom: 12px;
}

.code-block {
  background: #f5f5f5;
  border-radius: 4px;
  padding: 12px;
  overflow-x: auto;
  font-family: monospace;
  white-space: pre-wrap;
  word-wrap: break-word;
  text-align: left;  /* 新增：强制文本左对齐 */
  direction: ltr;    /* 新增：确保从左到右的阅读方向 */
}
</style>